#!/usr/bin/env ruby
#
# Created by Jerry Luk on 2008-05-04
# Copyright (c) 2008. All rights reserved.

module Google
  # GCalService extends the GDataService to streamline Google Calendar operations
  # 
  # == Authentication
  # 
  # GCalService supports two kind of authentications: AuthSub and client login.
  # 
  # ==== Using AuthSub
  # 
  # First you have to generate the AuthSub url
  #   service = Google::::GCalService.new
  #   auth_sub_url = service.generate_auth_sub_url('http://example.com/return_url')
  # When user go to the auth_sub_url and approves the service, google will return the token
  # to the url you specify with the parameter 'token'. In the example, it will be
  # <tt>http://localhost:3000/?token=the_auth_token</tt>
  # Set the token the service object to use the GCalService
  #   service.auth_token = auth_token
  # The token is a one-time token, if you want to get a long-lived session token, do::
  #   service.upgrade_auth_token
  # Now that you have the session token, you can save it
  #   session_token = service.session_token
  # To use the session token later
  #   service.session_token = session_token
  # 
  # ==== Use Client Login
  # 
  # Call the client_login method with the username and password
  #   service.client_login('user@gmail.com', 'password', 'http://www.example.com')
  # 
  # == Examples
  # 
  #   # Prints all the calendar names
  #   all_calendars_feed = service.get_all_calendars_feed
  #   all_calendars_feed.calendars.each { |c| puts c.title }
  # 
  #   # Prints all the event names for the default calendar
  #   default_calendar = service.get_default_calendar
  #   default_calendar.events.each { |e| puts e.title }
  # 
  #   # Creates a new calendar
  #   calendar = Google::::GCalendar.new
  #   calendar.title = "New Calendar"
  #   calendar.selected = true
  #   service.insert_calendar(calendar)
  # 
  #   # CRUD an event
  #   event = Google::::GEvent.new
  #   event.title = "Harris loves dancing"
  #   event.start_time = Time.now
  #   event.end_time = 2.hours.from_now
  #   event = service.insert_event(event)
  #   event = service.get_event(event.link)
  #   event.title = "Harris loves dancing so much"
  #   event = service.update_event(event)
  #   service.delete_event(event)
  #   
  class GCalService < GDataService
    DEFAULT_URI = '/calendar/feeds/default/private/full'
    
    def initialize()
      super
      @server = 'http://www.google.com'
      @scope = 'http://www.google.com/calendar/feeds/'
    end
    
    # Gets the feed of all calendars the user owns or subscribes to
    def get_all_calendars_feed(uri='/calendar/feeds/default/allcalendars/full', params={})
      GCalendarsFeed.new(self.get(uri, params))
    end
    
    # Gets the feed of all the calendars the user owns
    def get_own_calendars_feed(uri='/calendar/feeds/default/owncalendars/full', params={})
      GCalendarsFeed.new(self.get(uri, params))
    end
    
    # Authenticates the user and sets the GData Auth token for using the
    # Google Calendar services
    # 
    # Login retreives a temporary auth token which must be used with all
    # requests to GData services. The auth token is stored in the GData client
    # object
    # 
    # ==== Options
    # +username+:: The username of the account
    # +password+:: The password of the account
    # +source+:: Optional. The URL for your service
    # +account_type+:: Optional. The default is 'HOSTED_OR_GOOGLE'
    #
    def client_login(username, password, source=nil, account_type=nil)
      super(username, password, 'cl', source, account_type)
    end
    
    # Performs a query and returns a GData object.
    # See http://code.google.com/apis/calendar/reference.html#Parameters
    # for optional parameters
    # 
    # ==== Options
    # +uri+:: The query in the form of a URI.
    # +params+:: Additional URL parameters to be included
    #         in the URI. These are translated into query arguments
    #         in the form '&dict_key=value&...'.
    #         (e.g., ) <tt>{'max-results': '250'}</tt> would yield a
    #         parameter <tt>&max-results=250</tt>
    #
    def query(uri, params={})
      self.get(uri, params)
    end
    
    # Gets the event given a URI
    # 
    # ==== Options
    # +uri+:: The URI of the event. It is usually the atom_id
    # +params+:: Additional URL parameters to be included
    #         in the URI.
    #
    def get_event(uri, params={})
      GEvent.new(self.get(uri, params))
    end
    
    # Creates an event from a string
    # 
    # ==== Options
    # +text+:: The string describing the event. (e.g., )
    #          <tt>'Dinner 7pm'</tt>
    # +insert_uri+:: Optional. The calendar feed where the event
    #                to be inserted
    # +params+:: Additional URL parameters to be included
    #            in the URI.
    #
    def quick_add(text, insert_uri=DEFAULT_URI, params={})
      content = <<EOF
<entry xmlns='http://www.w3.org/2005/Atom' xmlns:gCal='http://schemas.google.com/gCal/2005'>
  <content type="html">#{text}</content>
  <gCal:quickadd value="true"/>
</entry>
EOF
      GEvent.new(self.post(content, insert_uri, params))
    end
    
    # Adds an event to Google Calendar
    #
    # ==== Options
    # +new_event+:: XML or GEvent which is to be added
    # +insert_uri+:: Optional. The calendar feed where the event
    #                to be inserted
    # +params+:: Additional URL parameters to be included
    #            in the URI.
    #
    def insert_event(new_event, insert_uri=DEFAULT_URI, params={})
      GEvent.new(self.post(new_event, insert_uri, params))
    end
    
    # Removes an event with the specified ID from Google Calendar
    #
    # ==== Options
    # +edit_uri_or_event+:: The edit URL of the entry or the GEvent
    #                       which is to be deleted. (e.g., )
    #                       <tt>'http://www.google.com/calendar/feed/defaults/private/full/abx'</tt>
    # +params+:: Additional URL parameters to be included
    #            in the URI.
    #
    def delete_event(edit_uri_or_event, params={})
      edit_uri_or_event = edit_uri_or_event.edit_link if edit_uri_or_event.kind_of? GEvent
      self.delete(edit_uri_or_event, params)
    end
    
    # Updates an existing event on the Google Calendar
    #
    # ==== Options
    # +updated_event+:: XML or GEvent which is to be updated
    # +params+:: Additional URL parameters to be included
    #            in the URI.
    #
    def update_event(updated_event, params={})
      GEvent.new(self.put(updated_event, updated_event.edit_link, params))
    end
    
    # Gets user's default calendar
    def get_default_calendar(params={})
      GCalendar.new(self.get(DEFAULT_URI, params))
    end
    
    # Get the calendar with the given ID
    def get_calendar(uri, params={})
      GCalendar.new(self.get(uri, params))
    end
    
    # Subscribes the authenticated to the provided calendar.
    #
    # ==== Options
    # +calendar+:: The calendar to which the user should be subscribed
    # +params+:: Additional URL parameters to be included in the 
    #            insertion request.
    def insert_calendar_subscription(calendar, params={})
      insert_uri = '/calendar/feeds/default/allcalendars/full'
      GCalendar.new(self.post(calendar, insert_uri, params))
    end
    
    # Creates a new calendar
    #
    # ==== Options
    # +calendar+:: The calendar to be created
    # +params+:: Additional URL parameters to be included in the 
    #           insertion request.
    def insert_calendar(calendar, params={})
      insert_uri = '/calendar/feeds/default/owncalendars/full'
      GCalendar.new(self.post(calendar, insert_uri, params))
    end
    
    # Updates a calendar
    #
    # ==== Options
    # +calendar+:: The calendar to be updated
    # +params+:: Additional URL parameters to be included in the 
    #           insertion request.
    def update_calendar(calendar, params={})
      edit_uri = calendar.edit_link
      GCalendar.new(self.put(calendar, edit_uri, params))
    end
    
    # Deletes a calendar
    # 
    # ==== Options
    # +edit_uri_or_calendar+:: The edit URL of the entry or the GEvent
    #                       which is to be deleted.
    # +params+:: Additional URL parameters to be included
    #            in the URI.
    #
    def delete_calendar(edit_uri_or_calendar, params={})
      edit_uri_or_calendar = edit_uri_or_calendar.edit_link if edit_uri_or_calendar.kind_of? GCalendar
      self.delete(edit_uri_or_calendar, params)
    end
    
    # Adds a calendar gadget to Google Calendar which Google will
    # turn it into a all-day event
    #
    # ==== Options
    # +gadget+:: GCalGadget which is to be added
    # +insert_uri+:: Optional. The calendar feed where the gadget
    #                to be inserted
    # +params+:: Additional URL parameters to be included
    #            in the URI.
    #
    def insert_gadget(gadget, insert_uri=DEFAULT_URI, params={})
      GEvent.new(self.post(gadget.to_xml, insert_uri, params))
    end
  end
end